use std::f64;

use crate::MathUtils::MathUtils;
use crate::Point::Point;
use crate::Segment::Segment;

/**
 * @author lianbo
 * @date 2020-10-20 17:07:35
 * @Description: 计算吸附的工具
 */

pub struct AdsorptionTool {}

impl AdsorptionTool {
    /**
     * @Description: 点到点的吸附点
     * @fn: 计算距离的方法，因为类型不确定
     * @accuracy 精度
     * @date 2018/11/05 14:52:16
     */
    pub fn find_adsorption_point(
        p: &Point,
        vs: &Vec<Point>,
        compare: &dyn Fn(&Point, &Point) -> f64,
        accuracy: f64,
        ep: Option<f64>,
    ) -> Option<Point> {
        let allowance_squared = accuracy * accuracy;
        let mut min = f64::MAX;
        let mut min_pos: Option<Point> = None;
        for v in vs {
            let dis: f64 = compare(p, v);
            if MathUtils::less_equal(dis, allowance_squared, ep) {
                if dis < min {
                    min = dis;
                    min_pos = Some(*v);
                }
            }
        }
        return min_pos;
    }

    /**
     * @Description: 寻找吸附的线段
     */
    pub fn find_adsorption_seg(
        p: &Point,
        segs: &Vec<Segment>,
        compare: &dyn Fn(&Point, &Segment) -> (f64, Point),
        accuracy: f64,
        ep: Option<f64>,
    ) -> (Option<Segment>, Option<Point>) {
        let mut min = f64::MAX;
        let mut closest_p = None;
        let mut closest_seg = None;
        for seg in segs {
            let result: (f64, Point) = compare(p, seg);
            if MathUtils::less_equal(result.0, accuracy, ep) {
                if result.0 < min {
                    min = result.0;
                    closest_p = Some(result.1);
                    closest_seg = Some(seg);
                }
            }
        }
        if closest_seg.is_some() {
            let closest_seg = closest_seg.unwrap().clone();
            return (Some(closest_seg), closest_p);
        }
        return (None, None);
    }

    pub fn find_horizontal_and_vertical(
        position: &Point,
        points: &Vec<Point>,
        accuracy: f64,
    ) -> (Option<f64>, Option<f64>) {
        let mut min_x = f64::MAX;
        let mut p_x = None;
        let mut min_y = f64::MAX;
        let mut p_y = None;
        for p in points {
            let offset_x = (position.x - p.x).abs();
            if offset_x < accuracy {
                if offset_x < min_x {
                    min_x = offset_x;
                    p_x = Some(p);
                }
            }

            let offset_y = (position.y - p.y).abs();
            if offset_y < accuracy {
                if offset_y < min_y {
                    min_y = offset_y;
                    p_y = Some(p);
                }
            }
        }
        let mut x = position.x;
        let mut y = position.y;
        if p_x.is_some() {
            x = p_x.unwrap().x;
        }
        if p_y.is_some() {
            y = p_y.unwrap().y;
        }
        if p_x.is_some() || p_y.is_some() {
            return (Some(x), Some(y));
        }
        return (None, None);
    }
}
